package com.demo.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.demo.entity.DroolsRule;
import com.demo.mapper.DroolsRuleMapper;
import com.demo.service.IDroolsRuleService;
import lombok.extern.slf4j.Slf4j;
import org.drools.compiler.kie.builder.impl.InternalKieModule;
import org.drools.compiler.kie.builder.impl.KieContainerImpl;
import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.Message;
import org.kie.api.builder.Results;
import org.kie.api.builder.model.KieBaseModel;
import org.kie.api.builder.model.KieModuleModel;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;

/**
 * @Classname DroolsRuleServiceImpl
 * @Description TODO
 * @Date 2023/11/23 12:05
 * @Created by Leo825
 */
@Service
@Slf4j
public class DroolsRuleServiceImpl extends ServiceImpl<DroolsRuleMapper, DroolsRule> implements IDroolsRuleService {

    @Resource
    private KieServices kieServices;
    // kie文件系统，需要缓存，如果每次添加规则都是重新new一个的话，则可能出现问题。即之前加到文件系统中的规则没有了
    @Resource
    private KieFileSystem kieFileSystem ;
    // 可以理解为构建 kmodule.xml
    @Resource
    private KieModuleModel kieModuleModel;
    // 需要全局唯一一个，如果每次加个规则都新创建一个，那么旧需要销毁之前创建的kieContainer，如果此时有正在使用的KieSession，则可能有问题
    @Resource
    private KieContainer kieContainer;

    /**
     * 规则数据库操作的mapper
     */
    @Autowired
    private DroolsRuleMapper droolsRuleMapper;


    @Override
    public boolean addDroolsRule(DroolsRule droolsRule) {
        return addOrUpdateRule(droolsRule) && droolsRuleMapper.insert(droolsRule) > 0;
    }

    @Override
    public boolean deleteDroolsRuleById(Long id) {
        return droolsRuleMapper.deleteById(id) > 0;
    }

    @Override
    public boolean updateDroolsRule(DroolsRule droolsRule) {
        return addOrUpdateRule(droolsRule) && droolsRuleMapper.updateById(droolsRule) > 0;
    }

    @Override
    public DroolsRule selectDroolsRuleById(Long id) {
        return droolsRuleMapper.selectById(id);
    }

    @Override
    public boolean reloadAllDroolsRule() {
        QueryWrapper<DroolsRule> queryWrapper = new QueryWrapper<>();
        List<DroolsRule> droolRuleList = droolsRuleMapper.selectList(queryWrapper);
        for (DroolsRule droolRule : droolRuleList) {
            addOrUpdateRule(droolRule);
        }
        return true;
    }

    @Override
    public List<DroolsRule> findAll() {
        QueryWrapper<DroolsRule> queryWrapper = new QueryWrapper<>();
        return droolsRuleMapper.selectList(queryWrapper);
    }



    /**
     * 判断该kbase是否存在
     *
     * @param kieBaseName
     * @return
     */
    @Override
    public boolean existsKieBase(String kieBaseName) {
        Collection<String> kieBaseNames = kieContainer.getKieBaseNames();
        if (kieBaseNames.contains(kieBaseName)) {
            return true;
        }
        log.info("需要创建KieBase:{}", kieBaseName);
        return false;
    }

    @Override
    public boolean executeRules(Long id, Object obj) {
        boolean result = false;
        DroolsRule droolsRule = getById(id);
        String kieBaseName = droolsRule.getKieBaseName();
        if(existsKieBase(kieBaseName)){
            KieSession kieSession = getKieSessionBySessionName(kieBaseName);
            kieSession.insert(obj);
            kieSession.fireAllRules();
            kieSession.dispose();
            result = true;
        }else{
            log.error("规则集不存在：{}", kieBaseName);
        }
        return result;
    }

    @Override
    public void deleteRuleByName(Long id, String ruleName){
        DroolsRule droolsRule = selectDroolsRuleById(id);
        deleteDroolsRule(droolsRule.getKieBaseName(),droolsRule.getKiePackageName(),ruleName);
    }

    /**
     * 删除规则，也可以将数据中的数据删除
     *
     * @param kieBaseName
     * @param packageName
     * @param ruleName
     */
    private void deleteDroolsRule(String kieBaseName, String packageName, String ruleName) {
        if (existsKieBase(kieBaseName)) {
            KieBase kieBase = kieContainer.getKieBase(kieBaseName);
            kieBase.removeRule(packageName, ruleName);
            log.info("删除kieBase:[{}]包:[{}]下的规则:[{}]", kieBaseName, packageName, ruleName);
        }
    }

    /**
     * 添加或更新 drools 规则
     *
     * @param droolsRule
     */
    private boolean addOrUpdateRule(DroolsRule droolsRule) {
        boolean result = true;
        // 获取kbase的名称
        String kieBaseName = droolsRule.getKieBaseName();
        // 判断该kbase是否存在
        boolean existsKieBase = existsKieBase(kieBaseName);
        // 该对象对应kmodule.xml中的kbase标签
        KieBaseModel kieBaseModel;
        if (!existsKieBase) {
            // 创建一个kbase
            kieBaseModel = kieModuleModel.newKieBaseModel(kieBaseName);
            // 不是默认的kieBase
            kieBaseModel.setDefault(false);
            // 设置该KieBase需要加载的包路径
            kieBaseModel.addPackage(droolsRule.getKiePackageName());
            // 设置kieSession，不是默认session,通过名称区分是不同的规则文件
            kieBaseModel.newKieSessionModel(kieBaseName + "-session").setDefault(false);
        } else {
            // 获取到已经存在的kbase对象
            kieBaseModel = kieModuleModel.getKieBaseModels().get(kieBaseName);
            // 获取到packages
            List<String> packages = kieBaseModel.getPackages();
            if (!packages.contains(droolsRule.getKiePackageName())) {
                kieBaseModel.addPackage(droolsRule.getKiePackageName());
                log.info("kieBase:{}添加一个新的包:{}", kieBaseName, droolsRule.getKiePackageName());
            } else {
                kieBaseModel = null;
            }
        }
        String file = "src/main/resources/rules/" + droolsRule.getKieBaseName() + ".drl";
        log.info("加载虚拟规则文件:{}", file);
        kieFileSystem.write(file, droolsRule.getRuleContent());
        if (kieBaseModel != null) {
            String kmoduleXml = kieModuleModel.toXML();
            log.info("加载kmodule.xml:[\n{}]", kmoduleXml);
            kieFileSystem.writeKModuleXML(kmoduleXml);
        }
        KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);
        // 通过KieBuilder构建KieModule下所有的KieBase
        kieBuilder.buildAll();
        // 获取构建过程中的结果
        Results results = kieBuilder.getResults();
        // 获取错误信息
        List<Message> messages = results.getMessages(Message.Level.ERROR);
        if (null != messages && !messages.isEmpty()) {
            messages.forEach(message -> log.error(message.getText()));
            result = false;
            log.error("加载规则出现异常");
        } else {
            // KieContainer只有第一次时才需要创建，之后就是使用这个
            if (null == kieContainer) {
                kieContainer = kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId());
            } else {
                // 实现动态更新
                ((KieContainerImpl) kieContainer).updateToKieModule((InternalKieModule) kieBuilder.getKieModule());
            }
        }
        return result;
    }

    /**
     * 根据kiebase 名字获取kiesession
     *
     * @param kieBaseName
     * @return
     */
    private KieSession getKieSessionBySessionName(String kieBaseName) {
        KieSession kieSession = kieContainer.newKieSession(kieBaseName + "-session");
        return kieSession;
    }

    public KieServices getKieServices() {
        return kieServices;
    }

    public KieFileSystem getKieFileSystem() {
        return kieFileSystem;
    }

    public KieModuleModel getKieModuleModel() {
        return kieModuleModel;
    }

    public KieContainer getKieContainer() {
        return kieContainer;
    }

}
